package com.zmj.sy.mom.srv.aps.service.scheduler.resource.shift;

import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections4.ListUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;

@Getter
@Setter
public class MultiTimeRange {

    private static final MultiTimeRange empty = MultiTimeRange.of();

    /**
     * 顺序不相交的时间段，每个时间段非空
     */
    private List<TimeRange> timeRanges;

    private static MultiTimeRange of(){
        MultiTimeRange multiTimeRange = new MultiTimeRange();
        multiTimeRange.timeRanges = new ArrayList<>();
        return multiTimeRange;
    }

    public static MultiTimeRange of(TimeRange timeRange) {
        if (timeRange == null || timeRange.isEmpty()) {
            return empty();
        }
        return MultiTimeRange.of(Collections.singletonList(timeRange), true, true);
    }

    public static MultiTimeRange of(List<TimeRange> timeRanges, boolean ordered, boolean disjointed) {
        if (timeRanges == null || timeRanges.isEmpty()) {
            return empty();
        }

        if (!ordered) {
            timeRanges = new ArrayList<>(timeRanges);
            timeRanges.sort(Comparator.comparing(TimeRange::getStartTime));
        }

        if (!disjointed) {
            List<TimeRange> disjointedRanges = new ArrayList<>();
            TimeRange current = null;
            for (TimeRange timeRange : timeRanges) {
                if (timeRange.isEmpty()) {
                    continue;
                }
                if (current == null) {
                    current = timeRange;
                }
                List<TimeRange> union = current.union(timeRange).getTimeRanges();
                if (union.size() > 1) {
                    disjointedRanges.add(current);
                }
                disjointedRanges.addAll(union.subList(0, union.size() - 1));
                current = union.get(union.size() - 1);
            }
            if (current != null && !current.isEmpty()) {
                disjointedRanges.add(current);
            }
            timeRanges = disjointedRanges;
        }

        MultiTimeRange multiTimeRange = new MultiTimeRange();
        multiTimeRange.timeRanges = timeRanges;
        return multiTimeRange;
    }

    public static MultiTimeRange empty() {
        return empty;
    }

    public boolean isEmpty() {
        return timeRanges.isEmpty();
    }

    public Duration length() {
        return timeRanges.stream()
                .map(TimeRange::length)
                .reduce(Duration::plus)
                .orElse(Duration.ZERO);
    }

    /**
     * 交集
     */
    public MultiTimeRange intersect(TimeRange tr) {
        List<TimeRange> intersects = new ArrayList<>();
        for (TimeRange timeRange : timeRanges) {
            TimeRange intersect = timeRange.intersect(tr);
            if (!intersect.isEmpty()) {
                intersects.add(intersect);
            }
        }
        return MultiTimeRange.of(intersects, true, true);
    }

    /**
     * 交集
     */
    public MultiTimeRange intersect(MultiTimeRange other) {
        if (other == null || other.isEmpty()) {
            return empty();
        }
        List<TimeRange> intersect = new ArrayList<>();
        for (TimeRange timeRange : other.getTimeRanges()) {
            intersect.addAll(this.intersect(timeRange).getTimeRanges());
        }
        return MultiTimeRange.of(intersect, true, true);
    }

    /**
     * 并集
     */
    public MultiTimeRange union(TimeRange other) {
        List<TimeRange> union = new ArrayList<>(timeRanges);
        union.add(other);
        return MultiTimeRange.of(union, false, false);
    }

    /**
     * 并集
     */
    public MultiTimeRange union(MultiTimeRange other) {
        if (other == null || other.isEmpty()) {
            return this;
        }
        List<TimeRange> union = new ArrayList<>(timeRanges);
        union.addAll(other.getTimeRanges());
        return MultiTimeRange.of(union, false, false);
    }

    /**
     * 差集
     */
    public MultiTimeRange subtract(TimeRange subtrahend) {
        List<TimeRange> subtraction = new ArrayList<>();
        for (TimeRange timeRange : timeRanges) {
            subtraction.addAll(timeRange.subtract(subtrahend).getTimeRanges());
        }
        return MultiTimeRange.of(subtraction, true, true);
    }

    /**
     * 差集
     */
    public MultiTimeRange subtract(MultiTimeRange subtrahend) {
        MultiTimeRange subtraction = this;
        for (TimeRange subtrahendRange : subtrahend.getTimeRanges()) {
            subtraction = subtraction.subtract(subtrahendRange);
        }
        return subtraction;
    }

    public MultiTimeRange lengthBefore(Duration length, LocalDateTime endTime) {
        LinkedList<TimeRange> result = new LinkedList<>();
        for (int index = this.timeRanges.size() - 1; index > -1; index--) {
            if (length.compareTo(Duration.ZERO) <= 0) {
                break;
            }
            TimeRange timeRange = timeRanges.get(index).lengthBefore(length, endTime);
            if (!timeRange.isEmpty()) {
                result.addFirst(timeRange);
                endTime = timeRange.getStartTime();
                length = length.minus(timeRange.length());
            }
        }
        return MultiTimeRange.of(result, true, true);
    }

    public LocalDateTime getStartTime() {
        if (timeRanges.isEmpty()) {
            return null;
        }
        return timeRanges.get(0).getStartTime();
    }

    public LocalDateTime getEndTime() {
        if (timeRanges.isEmpty()) {
            return null;
        }
        return timeRanges.get(timeRanges.size() -1).getEndTime();
    }

}
